﻿#region Usings 
using System;
using System.Drawing;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using ScrumTable.Common;
using ScrumTable.Common.Collections;
using ScrumTable.Common.Image;
using ScrumTable.Common.Logging;
using ScrumTable.Common.Unity;
using ScrumTable.UI.View.Controls;
using ScrumTable.UI.View.Converter;
using ScrumTable.UI.View.Util;
using ScrumTable.UI.ViewModel;
using ScrumTable.UI.ViewModel.Data;
using Image = System.Drawing.Image;

#endregion

namespace ScrumTable.UI.View.CustomControl
{
    /// <summary>
    /// Loads a image Async, while loading a loading gif is show
    /// </summary>
    public class WebAsyncImage : LoadingImageControl
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        protected readonly ScrumViewModelData ScrumData;
        protected static readonly Uri EmptyUri = new Uri(@"http://localhost.ch");
        private static readonly ImageSource _errorIcon;

        #endregion

        /// <summary>
        /// If this string is set, the request adds this information
        /// </summary>
        public string HttpPostInformation { get; set; }

        /// <summary>
        /// Uri to the Web Picture.
        /// </summary>
        public Uri UriBitmap
        {
            get { return (Uri)GetValue(UriBitmapProperty); }
            set { SetValue(UriBitmapProperty, value); }
        }

        public static readonly DependencyProperty UriBitmapProperty =
            DependencyProperty.Register(
                "UriBitmap", typeof(Uri), typeof(WebAsyncImage),
                new FrameworkPropertyMetadata(new Uri(@"http://localhost.ch"), new PropertyChangedCallback(OnWebUriChange)));



        public static readonly DependencyProperty WebPostWebPairProperty =
            DependencyProperty.Register(
                "WebPostWebPair", typeof(Pair<Uri, string>), typeof(WebAsyncImage),
                new FrameworkPropertyMetadata(new Pair<Uri, string>(null,null), OnWebPairChanged));
        

        public Pair<Uri,string> WebPostWebPair 
        {
            get { return (Pair<Uri, string>)GetValue(WebPostWebPairProperty); }
            set { SetValue(WebPostWebPairProperty, value); }
        }

        /// <summary>
        /// If set true a white border gets removed (right und bottom side)
        /// </summary>
        public bool ClipWhiteBorderRightBottom { get; set; }


        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        static WebAsyncImage()
        {
            VisualBrush errorIcon = (VisualBrush)Application.Current.Resources["IconError"];
            RenderTargetBitmap bitmapImage = new RenderTargetBitmap(800, 800, 250, 250, PixelFormats.Pbgra32);
            bitmapImage.Render(errorIcon.Visual);
            _errorIcon = bitmapImage;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="WebAsyncImage"/> class.
        /// </summary>
        public WebAsyncImage()
        {
            ClipWhiteBorderRightBottom = false;
            if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
            {
                ScrumData = ((IAppScreen)Application.Current.MainWindow).Container.Resolve<ScrumViewModelData>();
            }
        }

        #endregion

        #region Methods
     
        protected void ShowWaitingImage()
        {
           ShowWaitControl();
        }

        protected virtual void LoadImageFromUrl(Uri imageUri)
        {
            if (imageUri == null || EmptyUri == imageUri)
            {
                ShowWebImage((Image)null);
            }
            else if (!imageUri.IsAbsoluteUri || imageUri.Scheme == UriUtil.FileSchema)
            {
                ShowWebImage(ImageUtil.GetImageFromFile(imageUri));
            }
            else if (imageUri.IsAbsoluteUri)
            {
                ShowWaitingImage();
                
                //user has logged out?
                if(ScrumData.DataContext==null)
                {
                    return;
                }

                WebRequest request = CreateRequest(imageUri);
                SendPostDataToServerAsync(
                    request,
                    succeeded =>
                    {
                        if (succeeded)
                        {
                            request.BeginGetResponse(OnLoadImage, request);
                        }
                        else
                        {
                            ShowWebImage((Image)null);
                        }
                    });
            }
        }

        protected virtual Image LoadImageFromUrlSync(Uri webUri)
        {
            if (webUri == null)
            {
                ShowImage(_errorIcon);
                return null;
            }

            if (webUri.IsAbsoluteUri)
            {
                WebRequest request = CreateRequest(webUri);

                if (SendPostDataToServer(request))
                {
                    return ImageUtil.GetImageFromRequest(request);
                }
                else
                {
                    ShowImage(_errorIcon);
                    return null;
                }
            }
            else 
            {
                return ImageUtil.GetImageFromFile(webUri);
            }
        }
       
        protected void ShowWebImage(Image image)
        {
            int width = 0;
            int height = 0;
            int x = 0;
            int y = 0;
            
            if(ClipWhiteBorderRightBottom && image !=null)
            {
                width = BitmapUtil.GetWidth(((Bitmap)image));
                x = BitmapUtil.GetStartX(((Bitmap)image));
                height = BitmapUtil.GetHeight(((Bitmap)image), width);
                y = BitmapUtil.GetStartY(((Bitmap)image), width);
            }
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
            {
                if (image == null)
                {
                    ShowImage(_errorIcon);
                }
                else
                {
                    if (width != 0)
                    {
                        ShowImage(BitmapUtil.CreateBitmapSource(((Bitmap)image),x, y, width, height));
                    }
                    else
                    {
                        ShowImage(BitmapUtil.CreateBitmapSource(((Bitmap) image)));
                    }
                }
            }));
        }

        protected void ShowWebImage(BitmapSource image)
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
            {
                if (image == null)
                {
                    ShowImage(_errorIcon);
                }
                else
                {
                    ShowImage(image);
                }
            }));
        }

        /// <summary>
        /// Adds the post information in a async way (use this method if the request itself is async)
        /// </summary>
        /// <param name="request"></param>
        /// <param name="callback">Gets synch called if there is no POST information; gets async called if there is post information</param>
        protected void SendPostDataToServerAsync(WebRequest request, Action<bool> callback)
        {
            if (!string.IsNullOrEmpty(HttpPostInformation))
            {
                var dataToPost = Encoding.UTF8.GetBytes(HttpPostInformation);

                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";
                request.ContentLength = dataToPost.Length;

                request.BeginGetRequestStream(result => callback(TrySendPostDataToServer((WebRequest)result.AsyncState, dataToPost)), request);
            }
            else
            {
                callback(true);
            }
        }

        protected bool SendPostDataToServer(WebRequest request)
        {
            if (!string.IsNullOrEmpty(HttpPostInformation))
            {
                var dataToPost = Encoding.UTF8.GetBytes(HttpPostInformation);

                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";
                request.ContentLength = dataToPost.Length;
                
                return TrySendPostDataToServer(request, dataToPost);
            }
            return true;
        }

        private WebRequest CreateRequest(Uri imageUri)
        {
            var request = WebRequest.Create(imageUri);
            request.Credentials = ScrumData.DataContext.ConnectionInformation.NetworkCredentials;
            return request;
        }

        private bool TrySendPostDataToServer(WebRequest request, byte[] dataToSend)
        {
            try
            {
                using (var dataStreamToServer = request.GetRequestStream())
                {
                    dataStreamToServer.Write(dataToSend, 0, dataToSend.Length);
                }
                return true;
            }
            catch (Exception reqException)
            {
                Logger<WebAsyncImage>.Warn(reqException);
                return false;
            }
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        private static void OnWebUriChange(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            var imageControl = (WebAsyncImage)obj;
            imageControl.LoadImageFromUrl((Uri)args.NewValue);
        }

        private static void OnWebPairChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            var imageControl = (WebAsyncImage)obj;
            var pair = ((Pair<Uri, string>)args.NewValue);
            imageControl.HttpPostInformation = (pair == null) ? null : pair.Right;

            var uri = (pair == null) ? null : pair.Left;
            if (imageControl.UriBitmap != uri)
            {
                imageControl.UriBitmap = uri;
            }
            else
            {
                imageControl.LoadImageFromUrl(uri);
            }
        }

        private void OnLoadImage(IAsyncResult result)
        {
            WebRequest request = (WebRequest) result.AsyncState;
            request.Credentials = ScrumData.DataContext.ConnectionInformation.NetworkCredentials;
            Image image = ImageUtil.GetImageFromRequest(request); // error resistent / secure call
            ShowWebImage(image);
        }

        #endregion 
    }
}
